﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Lucene.Net.Store;
using MyNote.Data.Models;
using System.IO;
using System.Diagnostics;

namespace MyNote.FullText
{
    public class DbDirectory : Lucene.Net.Store.Directory
    {
        private FullTextIndexContainer _fulltextDatabase;
        private IndexDbDirectory _dbDirectory;
        private RAMDirectory _lockHelperDirectory;

        public string Name
        {
            get { return _dbDirectory.Name; }
        }

        public DbDirectory(FullTextIndexContainer database)
        {
            if (database == null)
                throw new ArgumentNullException("database");

            _fulltextDatabase = database;
            _lockHelperDirectory = new RAMDirectory();
        }

        public DbDirectory(FullTextIndexContainer database, string name) : this(database)
        {
            _dbDirectory = FindFile(name);
        }

        public override void Close()
        {
            Trace.WriteLine("[Close]");
            lock (_fulltextDatabase)
            {
                _fulltextDatabase.SaveChanges();
                _lockHelperDirectory.Close();
            }
        }

        public override IndexOutput CreateOutput(string name)
        {
            Trace.WriteLine(string.Format("[CreateOutput] name = {0}", name));
            DeleteFile(name);

            var file = new IndexDbFile();
            lock (_fulltextDatabase)
            {
                _fulltextDatabase.AddToIndexDbDirectorySet(file);
                file.Name = name;
                file.LastWriteTime = DateTime.Now;
                file.Content = new byte[] { };
                _fulltextDatabase.SaveChanges();
            }

            return new SimpleDbIndexOutput(file);
        }

        public override void DeleteFile(string name)
        {
            Trace.WriteLine(string.Format("[DeleteFile] name = {0}", name));
            lock (_fulltextDatabase)
            {
                var files = _fulltextDatabase.IndexDbDirectorySet.Where(d => d.Name == name);
                foreach (var file in files)
                    _fulltextDatabase.DeleteObject(file);

                _fulltextDatabase.SaveChanges();
            }
        }

        public override bool FileExists(string name)
        {
            Trace.WriteLine(string.Format("[FileExists] name = {0}", name));
            lock (_fulltextDatabase)
            {
                return _fulltextDatabase.IndexDbDirectorySet.Any(d => d.Name == name);
            }
        }

        public override long FileLength(string name)
        {
            Trace.WriteLine(string.Format("[FileLength] name = {0}", name));
            var file = (IndexDbFile)FindFile(name);

            return file.Content.LongLength;
        }

        public override long FileModified(string name)
        {
            Trace.WriteLine(string.Format("[FileModified] name = {0}", name));
            var file = FindFile(name);
            return (long)file.LastWriteTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds; //{{LUCENENET-353}}
        }

        public override string[] List()
        {
            Trace.WriteLine("[List]");
            lock (this)
            {
                lock (_fulltextDatabase)
                {
                    var files = _fulltextDatabase.IndexDbDirectorySet;
                    var result = files.Select(f => f.Name).ToArray();

                    if (result.Length == 0)
                        return null;
                    else
                        return result;
                }
            }
        }

        public override Lock MakeLock(string name)
        {
            Trace.WriteLine(string.Format("[MakeLock] name = {0}", name));
            return _lockHelperDirectory.MakeLock(name);
        }

        public override IndexInput OpenInput(string name)
        {
            Trace.WriteLine(string.Format("[OpenInput] name = {0}", name));
            var file = FindFile(name);
            if (file == null)
                return null;

            return new SimpleDbIndexInput((IndexDbFile)file);
        }

        public override void RenameFile(string from, string to)
        {
            //Trace.WriteLine(string.Format("[RenameFile] from = {0}, to = {1}", from, to));
            //var file = FindFile(from);
            //file.Name = to;
            //lock (_fulltextDatabase)
            //{
            //    _fulltextDatabase.SaveChanges();
            //}
        }

        public override void TouchFile(string name)
        {
            Trace.WriteLine(string.Format("[TouchFile] name = {0}", name));
            var file = FindFile(name);
            file.LastWriteTime = DateTime.Now;
        }

        private IndexDbDirectory FindFile(string name)
        {
            Trace.WriteLine(string.Format("[FindFile] name = {0}", name));
            lock (_fulltextDatabase)
            {
                return _fulltextDatabase.IndexDbDirectorySet.FirstOrDefault(d => d.Name == name);
            }
        }

        public class SimpleDbIndexInput : IndexInput, ICloneable
        {
            private IndexDbFile _file;
            private MemoryStream _stream;
            private bool _isClone;

            public SimpleDbIndexInput(IndexDbFile file)
            {
                _file = file;
                _stream = new MemoryStream(file.Content);
            }

            public override void Close()
            {
                Trace.WriteLine("[SimpleDbIndexInput.Close]");
                if (!_isClone)
                {
                    lock (_stream)
                    {
                        _stream.Close();
                    }
                }
            }

            public override long Length()
            {
                lock (_stream)
                {
                    var result = _stream.Length;
                    Trace.WriteLine(string.Format("[SimpleDbIndexInput.Length] returns {0}", result));
                    return result;
                }
            }

            public override object Clone()
            {
                var clone = (SimpleDbIndexInput)base.Clone();
                clone._file = _file;
                clone._stream = _stream;
                clone._isClone = true;
                // var clone = new SimpleDbIndexInput(_file);
                return clone;
            }

            public override long GetFilePointer()
            {
                lock (_stream)
                {
                    return _stream.Position;
                }
            }

            public override byte ReadByte()
            {
                Trace.WriteLine("[ReadByte]");
                lock (_stream)
                {
                    var b = _stream.ReadByte();
                    if (b == -1)
                        return 0;
                    else
                        return Convert.ToByte(b);
                }
            }

            public override void ReadBytes(byte[] buffer, int offset, int len)
            {
                Trace.WriteLine("[SimpleDbIndexInput.ReadBytes]");
                lock (_stream)
                {
                    _stream.Read(buffer, offset, len);
                }
            }

            public override void Seek(long pos)
            {
                Trace.WriteLine(string.Format("[SimpleDbIndexInput.Seek] pos = {0}", pos));
                lock (_stream)
                {
                    _stream.Seek(pos, SeekOrigin.Begin);
                }
            }
        }

        public class SimpleDbIndexOutput : IndexOutput, ICloneable
        {
            private IndexDbFile _file;
            private MemoryStream _stream;

            public SimpleDbIndexOutput(IndexDbFile file)
            {
                this._file = file;
                _stream = new MemoryStream();
            }

            public object Clone()
            {
                var clone = new SimpleDbIndexOutput(_file);
                return clone;
            }

            public override void Close()
            {
                Trace.WriteLine("[SimpleDbIndexOutput.Close]");
                Flush();
                _stream.Close();
            }

            public override void Flush()
            {
                lock (_stream)
                {
                    _file.Content = _stream.ToArray();
                }
            }

            public override long GetFilePointer()
            {
                lock (_stream)
                {
                    return _stream.Position;
                }
            }

            public override long Length()
            {
                lock (_stream)
                {
                    var result = _stream.Length;
                    Trace.WriteLine(string.Format("[SimpleDbIndexOutput.Length] returns {0}", result));
                    return result;
                }
            }

            public override void Seek(long pos)
            {
                Trace.WriteLine(string.Format("[SimpleDbIndexOutput.Seek] pos = {0}", pos));
                lock (_stream)
                {
                    _stream.Seek(pos, SeekOrigin.Begin);
                }
            }

            public override void WriteByte(byte value)
            {
                lock (_stream)
                {
                    _stream.WriteByte(value);
                }
            }

            public override void WriteBytes(byte[] buffer, int offset, int length)
            {
                Trace.WriteLine("[SimpleDbIndexOutput.WriteByte]");
                lock (_stream)
                {
                    _stream.Write(buffer, offset, length);
                }
            }
        }
    }
}